package config

import (
	"encoding/json"
	"fmt"
	"github.com/goinggo/mapstructure"
	"os"
	"path/filepath"
	"strings"
)

// 使用SetConfigure 参数rawVal函数时需要传递原始结构体，拒绝序列化之后的数据
type GooseConfig interface {
	GetConfigure(key string, rawVal interface{}) error
	SetConfigure(key string, rawVal interface{})
	GetConfigureBuffer(key string) (buf []byte, err error)
	SetConfigureBuffer(key string,buf []byte)(err error)
	CloseAndSave(configFile File) error
}

type gooseConfigure struct {
	Nodes         map[string]interface{}
	configureFile File
}

// 将配置文件的数据匹配至相应的结构体当中，rv 必须为目标结构体对象实例
func (g *gooseConfigure) GetConfigure(key string, rv interface{}) error {
	for k, v := range g.Nodes {
		if strings.ToLower(k) == strings.ToLower(key) {
			err := mapstructure.Decode(v, rv)
			if err != nil {
				return newError(err.Error())
			}
			return nil
		}
	}
	return newError(fmt.Sprintf("no such key of %s", key))
}

// 将原始结构体数据保存至配置文件对象
func (g *gooseConfigure) SetConfigure(key string, rv interface{}) {
	if g.Nodes == nil {
		g.Nodes = make(map[string]interface{}, 0)
	}
	g.Nodes[key] = rv
}

// 获取配置文件节点流数据,json 数据流
func (g *gooseConfigure) GetConfigureBuffer(key string) (buf []byte, err error) {
	v, ok := g.Nodes[strings.ToLower(key)]
	if !ok {
		return nil, newError(fmt.Sprintf("no any key of %s match", key))
	}

	buf, err = json.Marshal(v)
	if err != nil {
		return nil, newError(err.Error())
	}

	return buf, err

}

func (g *gooseConfigure) SetConfigureBuffer(key string,buf []byte) error {
	var jsonData map[string]interface{} = nil
	err := json.Unmarshal(buf,&jsonData)
	if err != nil {
		return err
	}
	if g.Nodes == nil {
		g.Nodes = make(map[string]interface{},0)
	}
	g.Nodes[strings.ToLower(key)] = jsonData
	return nil
}

// 保存配置文件，调用该函数之后该对象自动销毁，系统不能再调用
func (g *gooseConfigure) CloseAndSave(configFile File) error {
	// 若输入参数为空代表使用原来打开的配置文件
	if configFile == nil {
		configFile = g.configureFile
	}

	// 格式化并保存
	err := configFile.Marshal(g.Nodes)
	if err != nil {
		return newError(err.Error())
	}

	return nil
}

// 新建一个配置的结构体对象
func NewConfig(file string) (GooseConfig, error) {
	fp, err := openFile(file)
	if err != nil {
		return nil, withError(err, "open configure File failed")
	}

	g := &gooseConfigure{
		configureFile: fp,
	}
	err = fp.Unmarshal(&g.Nodes)
	if err != nil {
		return nil, withError(err, "parsing configure failed")
	}

	return g, nil
}

// 返回一个对应当前工作目录的根路径
func GetWorkPwd() (string, error) {
	return os.Getwd()
}

// 返回二进制文件所在的目录
func GetExecDir() (dir string, err error) {
	dir, err = filepath.Abs(filepath.Dir(os.Args[0]))
	return
}
